﻿using System;
using System.Text;

using LeanEngine.Data.Linq;

namespace LeanEngine.Data.SqlClient
{
    internal sealed class PagedQueryBuilder : IPagedQueryBuilder
    {
        #region Method Members

        private static string GetTableAlias(IQueryToken select)
        {
            var columns = select.GetStatement().Replace("SELECT ", string.Empty).Split(' ');
            var firstColumnSplitted = columns[0].Split('.');

            return firstColumnSplitted.Length == 1 ? string.Empty : firstColumnSplitted[0];
        }

        private static string GetOrderBy(IQueryToken orderby)
        {
            if (orderby == null) throw new LeanEngine.Data.Linq.Mapping.MappingException("You must specify an order by clause to this query work properly.");

            var orderbyValue = orderby.GetStatement();
            
            if (string.IsNullOrEmpty(orderbyValue)) throw new LeanEngine.Data.Linq.Mapping.MappingException("You must specify an order by clause to this query work properly.");

            return orderbyValue;
        }

        #endregion
        
        #region IPagedQueryBuilder Members

        public int PageIndex { get; set; }

        public int PageSize { get; set; }

        public string BuildTSQL(LinqQuery linqQuery)
        {
            var tableAlias = GetTableAlias(linqQuery.Select);
            var cmdText = new StringBuilder();

            cmdText.Append("WITH result_set AS \r\n( \r\n\t");
            cmdText.Append(linqQuery.Select.GetStatement());
            cmdText.Append("\r\n\t\t, ROW_NUMBER() OVER(");
            cmdText.Append(GetOrderBy(linqQuery.OrderBy));
            cmdText.Append(") AS [row_number] \r\n\t");
            cmdText.Append(linqQuery.From.GetStatement());
            cmdText.Append(Environment.NewLine);
            cmdText.Append("\t");
            if (linqQuery.JoinList != null)
            {
                foreach (var join in linqQuery.JoinList)
                {
                    cmdText.Append(join.GetStatement());
                    cmdText.Append(Environment.NewLine);
                }
                cmdText.Append("\t");
            }
            if (linqQuery.Where != null)
            {
                cmdText.Append(linqQuery.Where.GetStatement());
            }
            cmdText.Append("\r\n)\r\n SELECT ");
            cmdText.Append(tableAlias);
            cmdText.Append(".* FROM result_set ");
            cmdText.Append(tableAlias);
            cmdText.Append("\r\n WHERE ");
            cmdText.Append(SqlFunction.Instance.WrapInBrackets("row_number", tableAlias));
            cmdText.Append(" BETWEEN ");
            cmdText.Append((PageIndex * PageSize) + 1);
            cmdText.Append(" AND ");
            cmdText.Append((PageIndex * PageSize) + PageSize);

            return cmdText.ToString();
        }

        #endregion
    }
}
